#pragma once

#include <sqlite3.h>
#include <string>
#include <iostream>
#include "logger.hpp"
#include <vector>
#include <random>
#include <sstream>
#include <iomanip>
#include <atomic>
#include <sys/stat.h>
#include <errno.h>
#include <cstring>
#include <fstream>
#include <unistd.h>
class SqliteHelper
{
public:
    SqliteHelper(const std::string& dbfile)
        :_dbfile(dbfile), _handler(nullptr)
    {}
    bool open(int flags = SQLITE_OPEN_FULLMUTEX)
    {
        flags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
        int ret = sqlite3_open_v2(_dbfile.c_str(), &_handler, flags, nullptr);
        if(ret != SQLITE_OK)
        {
            ELOG("文件创建或者打开失败: %s", sqlite3_errmsg(_handler));
            return false;
        }
        return true;
    }
    bool exec(const std::string& sql, sqlite3_callback cb = nullptr, void* arg = nullptr)
    {
        int ret = sqlite3_exec(_handler, sql.c_str(), cb, arg, nullptr);
        if(ret != SQLITE_OK)
        {
            ELOG("%s执行失败: %s", sql.c_str(), sqlite3_errmsg(_handler));
            return false;
        }
        // DLOG("%s", sql.c_str());
        return true;
    }
    bool close()
    {
        if(_handler) sqlite3_close(_handler);
        return true;
    }   
    ~SqliteHelper()
    {
        close();        //不手动释放了
    }           
private:
    std::string _dbfile;
    sqlite3* _handler;
};

class SplitHelper
{
public:
    static int split(const std::string& str, const std::string& sep, std::vector<std::string>& result)
    {
        int pos = 0, idx = 0;
        while(idx < str.size())
        {
            pos = str.find(sep, idx);
            if(pos == std::string::npos)
            {
                result.push_back(str.substr(idx));
                break;
            }
            if(pos == idx) //idx这个位置就是sep
            {
                idx += sep.size();
                continue;
            }
            result.push_back(str.substr(idx, pos - idx));
            idx = pos + sep.size();
        }
        return result.size();
    }
};

class UUIDHelper
{
public:
    static std::string uuid()
    {
        std::random_device device;
        std::mt19937_64 mt(device());
        std::stringstream ss;
        std::uniform_int_distribution<int> dis(0, 255);
        for(int i = 0; i <= 7; i++)
        {
            ss << std::setw(2) << std::setfill('0') << std::hex << dis(mt);
            if(i == 3 || i == 5 || i == 7)
                ss << "-";
        }
        static std::atomic<long long> seq(1);
        long long num = seq.fetch_add(1);   
        for(int i = 7; i >= 0; i--)
        {
            ss << std::setw(2) << std::setfill('0') << std::hex << ((num >> (i * 8)) & 0xff);
            if(i == 6)
                ss << "-";
        }
        return ss.str();
    }
};

class FileHelper
{
public:
    FileHelper(const std::string& filename)
        :_filename(filename)
    {}
    bool exists()
    {
        return FileHelper::exists(_filename);
    }
    static bool exists(const std::string& filename)
    {
        struct stat st;
        return stat(filename.c_str(), &st) == 0; //只填一个文件名会去当前工作目录找
    }
    size_t size()   //64位下64位
    {
        struct stat st;
        if(stat(_filename.c_str(), &st) < 0)
        {
            ELOG("%s,文件大小查看失败, errno %d: %s", _filename.c_str(), errno, strerror(errno));
            return 0;
        }
        return st.st_size; //这是成员，我靠
    }
    bool read(char* buffer)
    {
        return read(buffer, 0, this->size());
    }
    // bool read(std::string& buffer)
    // {
    //     buffer.resize(this->size()); //reserve不行,因为他的大小还是0
    //     return read(&buffer[0], 0, this->size());
    // }
    bool read(std::string& buffer, long long offset = 0, size_t len = 0)
    {
        if(len == 0) len = this->size();                    //这里就非常妙啊,因为这个大小缺省不了，不然要加一个成员变量
        buffer.resize(len); //reserve不行,因为他的大小还是0
        return read(&buffer[0], offset, len);
    }
    bool read(char* buffer, long long offset, size_t len)
    {
        // errno = 0;
        std::ifstream ifs(_filename, std::ios::binary | std::ios::in); //以输入到程序  的方式打开文件
        if(ifs.is_open() == false)
        {
            ELOG("%s,文件读方式打开失败, errno %d: %s", _filename.c_str(), errno, strerror(errno));
            return false;
        }

        ifs.seekg(offset, std::ios::beg);

        ifs.read(buffer, len);
        // buffer[len] = 0;    //这里不需要给结尾0，因为他是string类型
        // DLOG("%s", buffer);
        if(ifs.good() == false)
        {
            ELOG("%s,文件读失败, errno %d: %s", _filename.c_str(), errno, strerror(errno));
            ifs.close();
            return false;
        }
        ifs.close();
        return true;
    }

    bool write(const std::string& buffer)   //大小不然拿不出来,目前感觉很鸡肋
    {
        return write(buffer.c_str(), 0, buffer.size());
    }
    bool write(const std::string& buffer, long long offset, size_t len)
    {
        return write(buffer.c_str(), offset, len);
    }
    bool write(const char* buffer, long long offset, size_t len)
    {
        // DLOG("%zu", len);
        // errno = 0;
        // DLOG("%d", errno);
        std::fstream fs(_filename, std::ios::binary | std::ios::in | std::ios::out); 
        if(fs.is_open() == false)
        {
            ELOG("%s,文件读写方式打开失败, errno %d: %s", _filename.c_str(), errno, strerror(errno));
            return false;
        }
        fs.seekp(offset, std::ios::beg);                    //修改为seekp


        fs.write(buffer, len);
        if(fs.good() == false)
        {
            ELOG("%s,文件写入失败, errno %d: %s", _filename.c_str(), errno, strerror(errno));
            fs.close();
            return false;
        }
        fs.close();
        return true;
    }

    bool rename(const std::string& newname)
    {
        return ::rename(_filename.c_str(), newname.c_str()) == 0;       //不写全局域可以找打吗？必须加，因为全局的rename被遮掩掉了
    }
    static bool createFile(const std::string& filename)
    {
        std::ofstream ofs(filename, std::ios::binary | std::ios::out | std::ios::app);   //就是这里的问题| std::ios::app

        if(ofs.is_open() == false)
        {
            ELOG("%s,文件方式打开失败, errno %d: %s", filename.c_str(), errno, strerror(errno));
            return false;
        }

        return true;
    }
    static bool removeFile(const std::string& filename)
    {
        return ::remove(filename.c_str()) == 0;
    }
    static bool createDirectory(const std::string& path) //这个参数的要求是什么？？？
    {                                                           //纯目录
        // DLOG("%d", errno);
        size_t pos = 0, idx = 0;                               //./aaa/bbb/ccc/
        while(idx < path.size())
        {
            pos = path.find("/", idx);
            std::string directory = path.substr(0, pos).c_str();
            if(idx == pos)
            {
                WLOG("目录格式不正确");
                break; //   .///
            }
            if(::mkdir(directory.c_str(), 0775) != 0 && errno != EEXIST)//选择用 errno != EEXIST 判断文件是否存在
            {
                ELOG("%s,目录创建失败, errno %d: %s", path.c_str(), errno, strerror(errno));
                errno = 0;      //不要让他影响后面，放这里其实改变不了什么
                return false;
            }
            if(pos == std::string::npos) 
            {
                // DLOG("Youma");
                break; //说明最后一个目录已经处理完了
            }
            idx = pos + 1;
        }
        errno = 0;
        return true;
    }
    static bool removeDirectory(const std::string& path)
    {
        std::string command = "rm -rf " + path;
        return ::system(command.c_str()) == 0; 
    }
    static std::string getParentDirectory(const std::string& filename) //./aaa/bbb/ccc/d.hpp
    {
        int pos = filename.rfind("/");
        if(pos == std::string::npos)
            return "./";
        return filename.substr(0, pos);
    }
    std::string getParentDirectory() //./aaa/bbb/ccc/d.hpp
    {
        return FileHelper::getParentDirectory(_filename);
    }
private:
    std::string _filename;
};
